package service

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"geekai/core/types"
	"mime"
	"net/smtp"
	"net/textproto"
)

// SmtpService 邮件模块
type SmtpService struct {
	config *types.SmtpConfig
}

func NewSmtpService(appConfig *types.AppConfig) *SmtpService {
	return &SmtpService{
		config: &appConfig.SmtpConfig,
	}
}

// SendVerifyCode 发送邮件
func (s *SmtpService) SendVerifyCode(to string, code int) error {
	subject := fmt.Sprintf("%s 注册验证码", s.config.AppName)
	body := fmt.Sprintf("【%s】：你的验证码为 %d，请不要告诉他人。如非本人操作，请忽略此邮件。", s.config.AppName, code)

	auth := smtp.PlainAuth("", s.config.From, s.config.Password, s.config.Host)
	if s.config.UseTls {
		return s.sendTLS(auth, to, subject, body)
	} else {
		return s.send(auth, to, subject, body)
	}
}

// sendTLS 通过TLS 加密连接发送邮件的 SMTP 客户端实现
func (s *SmtpService) sendTLS(auth smtp.Auth, to string, subject string, body string) error {
	// TLS 配置
	// ServerName 必须与 SMTP 服务器证书中的域名匹配（防止中间人攻击）
	// 默认使用 TLS 1.2+ 协议（Go 1.20+ 默认禁用 TLS 1.1 以下版本）
	// 未配置 InsecureSkipVerify: true，因此会验证证书有效性
	tlsConfig := &tls.Config{
		ServerName: s.config.Host,
	}
	// 建立TLS连接，创建加密的 TCP 连接，用于后续 SMTP 通信
	conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", s.config.Host, s.config.Port), tlsConfig)
	if err != nil {
		return fmt.Errorf("连接到SMTP服务器时出错：%v", err)
	}
	defer conn.Close()

	// 作用：基于 TLS 连接创建 SMTP 客户端对象
	// 特殊处理：使用 smtp.NewClient 而非直接 smtp.SendMail，实现更细粒度的控制
	// 参数：s.config.Host 用于 EHLO 命令的域名标识
	client, err := smtp.NewClient(conn, s.config.Host)
	if err != nil {
		return fmt.Errorf("创建SMTP客户端时出错：%v", err)
	}
	defer client.Quit()

	// 身份验证
	if err = client.Auth(auth); err != nil {
		return fmt.Errorf("身份验证错误：%v", err)
	}
	// 设置寄件人
	if err = client.Mail(s.config.From); err != nil {
		return fmt.Errorf("设置发送人时出错：%v", err)
	}
	// 设置收件人
	if err = client.Rcpt(to); err != nil {
		return fmt.Errorf("设置接收人时出错: %v", err)
	}
	// 发送邮件
	wc, err := client.Data()
	if err != nil {
		return fmt.Errorf("获取数据写入器时出错: %v", err)
	}
	defer wc.Close()
	// From 必须符合 RFC 5322 地址格式（如 "Name <user@domain.com>"）
	// Subject 应使用 MIME 编码处理非 ASCII 字符（当前代码未处理，需注意中文乱码问题）
	header := make(textproto.MIMEHeader)
	header.Set("From", s.config.From)
	header.Set("To", to)
	header.Set("Subject", subject)
	// 将邮件头写入
	for key, values := range header {
		for _, value := range values {
			_, err = fmt.Fprintf(wc, "%s: %s\r\n", key, value)
			if err != nil {
				return fmt.Errorf("发送电子邮件头时出错: %v", err)
			}
		}
	}
	// // 空行分隔 Header 和 Body
	_, _ = fmt.Fprintln(wc)
	// 将邮件内容写入
	_, err = fmt.Fprintf(wc, body)
	if err != nil {
		return fmt.Errorf("发送电子邮件时出错: %v", err)
	}
	// 发送完毕
	err = wc.Close()
	if err != nil {
		return fmt.Errorf("关闭数据写入器时出错: %v", err)
	}
	return nil
}

// send 发送邮件
// auth：SMTP 认证信息（如用户名、密码）。
// to：收件人邮箱地址。
// subject：邮件主题（需编码后使用）。
// body：邮件正文内容。
func (s *SmtpService) send(auth smtp.Auth, to string, subject string, body string) error {
	// 对主题进行MIME编码，确保特殊字符（如非 ASCII 字符、空格）符合邮件协议标准，避免乱码。
	// subject := "Hello 世界"
	// encodedSubject := "=?UTF-8?Q?Hello_=E4=B8=96=E7=95=8C?="
	encodedSubject := mime.QEncoding.Encode("UTF-8", subject)
	// 组装邮件 构建符合 SMTP 标准的邮件内容。
	message := bytes.NewBuffer(nil)
	// From：发件人显示名称（s.config.AppName）和邮箱地址（s.config.From）。
	message.WriteString(fmt.Sprintf("From: \"%s\" <%s>\r\n", s.config.AppName, s.config.From))
	// To：收件人地址。
	message.WriteString(fmt.Sprintf("To: %s\r\n", to))
	// Subject：编码后的主题。
	message.WriteString(fmt.Sprintf("Subject: %s\r\n", encodedSubject))
	message.WriteString("Content-Type: text/plain; charset=UTF-8\r\n")
	message.WriteString("\r\n" + body)
	// 发送邮件
	// 参数解析：
	//    服务器地址：s.config.Host:Port（如 smtp.example.com:587）。
	//	  认证信息：auth（包含用户名和密码）。
	//    发件人：s.config.From。
	//    收件人列表：[]string{to}（支持多个收件人，此处仅一个）。
	//    邮件内容：message.Bytes() 将缓冲区内容转为字节切片。
	err := smtp.SendMail(s.config.Host+":"+fmt.Sprint(s.config.Port), auth, s.config.From, []string{to}, message.Bytes())
	if err != nil {
		return fmt.Errorf("发送邮件失败：%v", err)
	}
	return err
}
